var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

import React from 'react';
import AbstractSeries from './series/abstract-series';
import { getAttributeScale } from '../utils/scales-utils';
import PropTypes from 'prop-types';

function getLocs(evt) {
  var xLoc = evt.type === 'touchstart' ? evt.pageX : evt.offsetX;
  var yLoc = evt.type === 'touchstart' ? evt.pageY : evt.offsetY;
  return { xLoc: xLoc, yLoc: yLoc };
}

var Highlight = function (_AbstractSeries) {
  _inherits(Highlight, _AbstractSeries);

  function Highlight() {
    var _ref;

    var _temp, _this, _ret;

    _classCallCheck(this, Highlight);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Highlight.__proto__ || Object.getPrototypeOf(Highlight)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
      dragging: false,
      brushArea: { top: 0, right: 0, bottom: 0, left: 0 },
      brushing: false,
      startLocX: 0,
      startLocY: 0,
      dragArea: null
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  _createClass(Highlight, [{
    key: '_getDrawArea',
    value: function _getDrawArea(xLoc, yLoc) {
      var _state = this.state,
          startLocX = _state.startLocX,
          startLocY = _state.startLocY;
      var _props = this.props,
          enableX = _props.enableX,
          enableY = _props.enableY,
          highlightWidth = _props.highlightWidth,
          highlightHeight = _props.highlightHeight,
          innerWidth = _props.innerWidth,
          innerHeight = _props.innerHeight,
          marginLeft = _props.marginLeft,
          marginRight = _props.marginRight,
          marginBottom = _props.marginBottom,
          marginTop = _props.marginTop;

      var plotHeight = innerHeight + marginTop + marginBottom;
      var plotWidth = innerWidth + marginLeft + marginRight;
      var touchWidth = highlightWidth || plotWidth;
      var touchHeight = highlightHeight || plotHeight;

      return {
        bottom: enableY ? Math.max(startLocY, yLoc) : touchHeight,
        right: enableX ? Math.max(startLocX, xLoc) : touchWidth,
        left: enableX ? Math.min(xLoc, startLocX) : 0,
        top: enableY ? Math.min(yLoc, startLocY) : 0
      };
    }
  }, {
    key: '_getDragArea',
    value: function _getDragArea(xLoc, yLoc) {
      var _props2 = this.props,
          enableX = _props2.enableX,
          enableY = _props2.enableY;
      var _state2 = this.state,
          startLocX = _state2.startLocX,
          startLocY = _state2.startLocY,
          dragArea = _state2.dragArea;


      return {
        bottom: dragArea.bottom + (enableY ? yLoc - startLocY : 0),
        left: dragArea.left + (enableX ? xLoc - startLocX : 0),
        right: dragArea.right + (enableX ? xLoc - startLocX : 0),
        top: dragArea.top + (enableY ? yLoc - startLocY : 0)
      };
    }
  }, {
    key: '_clickedOutsideDrag',
    value: function _clickedOutsideDrag(xLoc, yLoc) {
      var _props3 = this.props,
          enableX = _props3.enableX,
          enableY = _props3.enableY;
      var _state3 = this.state,
          dragArea = _state3.dragArea,
          _state3$brushArea = _state3.brushArea,
          left = _state3$brushArea.left,
          right = _state3$brushArea.right,
          top = _state3$brushArea.top,
          bottom = _state3$brushArea.bottom;

      var clickedOutsideDragX = dragArea && (xLoc < left || xLoc > right);
      var clickedOutsideDragY = dragArea && (yLoc < top || yLoc > bottom);
      if (enableX && enableY) {
        return clickedOutsideDragX || clickedOutsideDragY;
      }
      if (enableX) {
        return clickedOutsideDragX;
      }
      if (enableY) {
        return clickedOutsideDragY;
      }
      return true;
    }
  }, {
    key: '_convertAreaToCoordinates',
    value: function _convertAreaToCoordinates(brushArea) {
      // NOTE only continuous scales are supported for brushing/getting coordinates back
      var _props4 = this.props,
          enableX = _props4.enableX,
          enableY = _props4.enableY,
          marginLeft = _props4.marginLeft,
          marginTop = _props4.marginTop;

      var xScale = getAttributeScale(this.props, 'x');
      var yScale = getAttributeScale(this.props, 'y');

      // Ensure that users wishes are being respected about which scales are evaluated
      // this is specifically enabled to ensure brushing on mixed categorical and linear
      // charts will run as expected

      if (enableX && enableY) {
        return {
          bottom: yScale.invert(brushArea.bottom),
          left: xScale.invert(brushArea.left - marginLeft),
          right: xScale.invert(brushArea.right - marginLeft),
          top: yScale.invert(brushArea.top)
        };
      }

      if (enableY) {
        return {
          bottom: yScale.invert(brushArea.bottom - marginTop),
          top: yScale.invert(brushArea.top - marginTop)
        };
      }

      if (enableX) {
        return {
          left: xScale.invert(brushArea.left - marginLeft),
          right: xScale.invert(brushArea.right - marginLeft)
        };
      }

      return {};
    }
  }, {
    key: 'startBrushing',
    value: function startBrushing(e) {
      var _this2 = this;

      var _props5 = this.props,
          onBrushStart = _props5.onBrushStart,
          onDragStart = _props5.onDragStart,
          drag = _props5.drag;
      var dragArea = this.state.dragArea;

      var _getLocs = getLocs(e.nativeEvent),
          xLoc = _getLocs.xLoc,
          yLoc = _getLocs.yLoc;

      var startArea = function startArea(dragging, resetDrag) {
        var emptyBrush = {
          bottom: yLoc,
          left: xLoc,
          right: xLoc,
          top: yLoc
        };
        _this2.setState({
          dragging: dragging,
          brushArea: dragArea && !resetDrag ? dragArea : emptyBrush,
          brushing: !dragging,
          startLocX: xLoc,
          startLocY: yLoc
        });
      };

      var clickedOutsideDrag = this._clickedOutsideDrag(xLoc, yLoc);
      if (drag && !dragArea || !drag || clickedOutsideDrag) {
        startArea(false, clickedOutsideDrag);

        if (onBrushStart) {
          onBrushStart(e);
        }
        return;
      }

      if (drag && dragArea) {
        startArea(true, clickedOutsideDrag);
        if (onDragStart) {
          onDragStart(e);
        }
      }
    }
  }, {
    key: 'stopBrushing',
    value: function stopBrushing(e) {
      var _state4 = this.state,
          brushing = _state4.brushing,
          dragging = _state4.dragging,
          brushArea = _state4.brushArea;
      // Quickly short-circuit if the user isn't brushing in our component

      if (!brushing && !dragging) {
        return;
      }
      var _props6 = this.props,
          onBrushEnd = _props6.onBrushEnd,
          onDragEnd = _props6.onDragEnd,
          drag = _props6.drag;

      var noHorizontal = Math.abs(brushArea.right - brushArea.left) < 5;
      var noVertical = Math.abs(brushArea.top - brushArea.bottom) < 5;
      // Invoke the callback with null if the selected area was < 5px
      var isNulled = noVertical || noHorizontal;
      // Clear the draw area
      this.setState({
        brushing: false,
        dragging: false,
        brushArea: drag ? brushArea : { top: 0, right: 0, bottom: 0, left: 0 },
        startLocX: 0,
        startLocY: 0,
        dragArea: drag && !isNulled && brushArea
      });

      if (brushing && onBrushEnd) {
        onBrushEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null);
      }

      if (drag && onDragEnd) {
        onDragEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null);
      }
    }
  }, {
    key: 'onBrush',
    value: function onBrush(e) {
      var _props7 = this.props,
          onBrush = _props7.onBrush,
          onDrag = _props7.onDrag,
          drag = _props7.drag;
      var _state5 = this.state,
          brushing = _state5.brushing,
          dragging = _state5.dragging;

      var _getLocs2 = getLocs(e.nativeEvent),
          xLoc = _getLocs2.xLoc,
          yLoc = _getLocs2.yLoc;

      if (brushing) {
        var brushArea = this._getDrawArea(xLoc, yLoc);
        this.setState({ brushArea: brushArea });

        if (onBrush) {
          onBrush(this._convertAreaToCoordinates(brushArea));
        }
      }

      if (drag && dragging) {
        var _brushArea = this._getDragArea(xLoc, yLoc);
        this.setState({ brushArea: _brushArea });
        if (onDrag) {
          onDrag(this._convertAreaToCoordinates(_brushArea));
        }
      }
    }
  }, {
    key: 'render',
    value: function render() {
      var _this3 = this;

      var _props8 = this.props,
          color = _props8.color,
          className = _props8.className,
          highlightHeight = _props8.highlightHeight,
          highlightWidth = _props8.highlightWidth,
          highlightX = _props8.highlightX,
          highlightY = _props8.highlightY,
          innerWidth = _props8.innerWidth,
          innerHeight = _props8.innerHeight,
          marginLeft = _props8.marginLeft,
          marginRight = _props8.marginRight,
          marginTop = _props8.marginTop,
          marginBottom = _props8.marginBottom,
          opacity = _props8.opacity;
      var _state$brushArea = this.state.brushArea,
          left = _state$brushArea.left,
          right = _state$brushArea.right,
          top = _state$brushArea.top,
          bottom = _state$brushArea.bottom;


      var leftPos = 0;
      if (highlightX) {
        var xScale = getAttributeScale(this.props, 'x');
        leftPos = xScale(highlightX);
      }

      var topPos = 0;
      if (highlightY) {
        var yScale = getAttributeScale(this.props, 'y');
        topPos = yScale(highlightY);
      }

      var plotWidth = marginLeft + marginRight + innerWidth;
      var plotHeight = marginTop + marginBottom + innerHeight;
      var touchWidth = highlightWidth || plotWidth;
      var touchHeight = highlightHeight || plotHeight;

      return React.createElement(
        'g',
        {
          transform: 'translate(' + leftPos + ', ' + topPos + ')',
          className: className + ' rv-highlight-container'
        },
        React.createElement('rect', {
          className: 'rv-mouse-target',
          fill: 'black',
          opacity: '0',
          x: '0',
          y: '0',
          width: Math.max(touchWidth, 0),
          height: Math.max(touchHeight, 0),
          onMouseDown: function onMouseDown(e) {
            return _this3.startBrushing(e);
          },
          onMouseMove: function onMouseMove(e) {
            return _this3.onBrush(e);
          },
          onMouseUp: function onMouseUp(e) {
            return _this3.stopBrushing(e);
          },
          onMouseLeave: function onMouseLeave(e) {
            return _this3.stopBrushing(e);
          }
          // preventDefault() so that mouse event emulation does not happen
          , onTouchEnd: function onTouchEnd(e) {
            e.preventDefault();
            _this3.stopBrushing(e);
          },
          onTouchCancel: function onTouchCancel(e) {
            e.preventDefault();
            _this3.stopBrushing(e);
          },
          onContextMenu: function onContextMenu(e) {
            return e.preventDefault();
          },
          onContextMenuCapture: function onContextMenuCapture(e) {
            return e.preventDefault();
          }
        }),
        React.createElement('rect', {
          className: 'rv-highlight',
          pointerEvents: 'none',
          opacity: opacity,
          fill: color,
          x: left,
          y: top,
          width: Math.min(Math.max(0, right - left), touchWidth),
          height: Math.min(Math.max(0, bottom - top), touchHeight)
        })
      );
    }
  }]);

  return Highlight;
}(AbstractSeries);

Highlight.displayName = 'HighlightOverlay';
Highlight.defaultProps = {
  color: 'rgb(77, 182, 172)',
  className: '',
  enableX: true,
  enableY: true,
  opacity: 0.3
};

Highlight.propTypes = _extends({}, AbstractSeries.propTypes, {
  enableX: PropTypes.bool,
  enableY: PropTypes.bool,
  highlightHeight: PropTypes.number,
  highlightWidth: PropTypes.number,
  highlightX: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  highlightY: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  onBrushStart: PropTypes.func,
  onDragStart: PropTypes.func,
  onBrush: PropTypes.func,
  onDrag: PropTypes.func,
  onBrushEnd: PropTypes.func,
  onDragEnd: PropTypes.func
});

export default Highlight;